package com.brillilab.service.logic.experiments;

import com.brillilab.common.constant.ResultEnum;
import com.brillilab.common.exceptions.BrillilabException;
import com.brillilab.common.utils.MyBeanUtils;
import com.brillilab.domain.constants.experiment.PlateTypeCodeConstant;
import com.brillilab.domain.enums.common.BoolEnum;
import com.brillilab.domain.enums.experiment.ExperimentStepMaterialTypeEnum;
import com.brillilab.domain.enums.experiment.PlateContentTypeEnum;
import com.brillilab.domain.enums.experiment.PlateTypeEnum;
import com.brillilab.domain.enums.kit.KitEnum;
import com.brillilab.domain.pa.experiments.PlateAdd;
import com.brillilab.domain.pa.experiments.PlateReagentAdd;
import com.brillilab.domain.pa.experiments.PlateTextAdd;
import com.brillilab.domain.pa.experiments.PlateTimerAdd;
import com.brillilab.domain.po.experiments.*;
import com.brillilab.domain.po.experiments.Experiment;
import com.brillilab.domain.po.system.Dict;
import com.brillilab.domain.vo.experiments.*;
import com.brillilab.service.core.experiments.*;
import com.brillilab.service.core.experiments.IExperimentService;
import com.brillilab.service.core.system.IDictService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static java.util.stream.Collectors.toList;

/**
 * 多孔板逻辑
 *
 * @author Wu Menghao
 * @time 2019/04/16
 */
@Service
@Transactional
public class ExperimentPlateLogic {

    @Resource
    private IExperimentService experimentService;
    @Resource
    private IExperimentPhaseService experimentPhaseService;
    @Resource
    private IExperimentStepService experimentStepService;
    @Resource
    private IExperimentStepTimerService experimentStepTimerService;
    @Resource
    private IExperimentStepReagentService experimentStepReagentService;
    @Resource
    private IExperimentStepTextService experimentStepTextService;
    @Resource
    private IExperimentBillService experimentBillService;
    @Resource
    private ExperimentStepTimerLogic experimentStepTimerLogic;

    @Resource
    private IPlateService plateService;
    @Resource
    private IPlateStepRelateService plateStepRelateService;
    @Resource
    private IPlateGridService plateGridService;
    @Resource
    private IPlateGridTimerService plateGridTimerService;
    @Resource
    private IPlateGridReagentService plateGridReagentService;
    @Resource
    private IPlateGridTextService plateGridTextService;
    @Resource
    private IPlateGridGroupService plateGridGroupService;

    @Resource
    private IDictService dictService;
    @Resource
    private IExperimentGroupService experimentGroupService;

    /**
     * 多孔板类型列表
     *
     * @return
     */
    public List<PlateTypeGroupVo> getPlateTypeGroups() {
        List<Dict> plateTypes = dictService.getPlateType();

        ArrayList<PlateTypeVo> plateTypeVos = new ArrayList<>();
        plateTypes.forEach(e -> {
            String type = e.getType();
            Integer code = e.getCode();
            PlateTypeVo plateTypeVo = new PlateTypeVo();
            plateTypeVo.setId(e.getId());
            plateTypeVo.setName(e.getName());
            plateTypeVo.setType(PlateTypeEnum.getValue(e.getType()));
            plateTypeVo.setCode(e.getCode());
            if (type.equals(PlateTypeEnum.PORE_PLATE.getName())) {
                if (code.equals(PlateTypeCodeConstant.PORE6)) {
                    plateTypeVo.setColumns(3);
                    plateTypeVo.setRows(2);
                } else if (code.equals(PlateTypeCodeConstant.PORE12)) {
                    plateTypeVo.setColumns(4);
                    plateTypeVo.setRows(3);
                } else if (code.equals(PlateTypeCodeConstant.PORE24)) {
                    plateTypeVo.setColumns(6);
                    plateTypeVo.setRows(4);
                } else if (code.equals(PlateTypeCodeConstant.PORE48)) {
                    plateTypeVo.setColumns(8);
                    plateTypeVo.setRows(6);
                } else if (code.equals(PlateTypeCodeConstant.PORE96)) {
                    plateTypeVo.setColumns(12);
                    plateTypeVo.setRows(8);
                } else if (code.equals(PlateTypeCodeConstant.PORE384)) {
                    plateTypeVo.setColumns(24);
                    plateTypeVo.setRows(16);
                } else if (code.equals(PlateTypeCodeConstant.ONE)) {
                    plateTypeVo.setColumns(1);
                    plateTypeVo.setRows(1);
                } else {
                    throw new BrillilabException("类型不存在!");
                }
            } else if (type.equals(PlateTypeEnum.GUM_PLATE.getName())) {
                if (code.equals(PlateTypeCodeConstant.GUM10)) {
                    plateTypeVo.setColumns(10);
                    plateTypeVo.setRows(1);
                } else if (code.equals(PlateTypeCodeConstant.GUM15)) {
                    plateTypeVo.setColumns(15);
                    plateTypeVo.setRows(1);
                } else if (code.equals(PlateTypeCodeConstant.GUM20)) {
                    plateTypeVo.setColumns(20);
                    plateTypeVo.setRows(1);
                } else if (code.equals(PlateTypeCodeConstant.GUM30)) {
                    plateTypeVo.setColumns(15);
                    plateTypeVo.setRows(2);
                } else if (code.equals(PlateTypeCodeConstant.GUM40)) {
                    plateTypeVo.setColumns(20);
                    plateTypeVo.setRows(2);
                } else {
                    throw new BrillilabException("类型不存在!");
                }
            } else if (type.equals(PlateTypeEnum.CULTURE_DISH.getName())
                    || type.equals(PlateTypeEnum.CULTURE_BOTTLER.getName())) {
                plateTypeVo.setColumns(1);
                plateTypeVo.setRows(1);
            } else {
                throw new BrillilabException("类型不存在!");
            }
            plateTypeVos.add(plateTypeVo);
        });

        return getPlateTypeGroupVos(plateTypeVos);
    }

    private List<PlateTypeGroupVo> getPlateTypeGroupVos(List<PlateTypeVo> plateTypeVos) {
        //分组
        List<PlateTypeVo> porePlate = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.PORE_PLATE.getValue()) && e.getCode() != PlateTypeCodeConstant.ONE).collect(toList());
        List<PlateTypeVo> gumPlate = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())).collect(toList());
        List<PlateTypeVo> others = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.CULTURE_DISH.getValue())
                || e.getType().equals(PlateTypeEnum.CULTURE_BOTTLER.getValue())
                || (e.getType().equals(PlateTypeEnum.PORE_PLATE.getValue()) && e.getCode() == PlateTypeCodeConstant.ONE)).collect(toList());
        List<PlateTypeGroupVo> groupVos = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            PlateTypeGroupVo plateTypeGroupVo = new PlateTypeGroupVo();
            switch (i) {
                case 1:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(porePlate);
                    break;
                case 2:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(gumPlate);
                    break;
                case 3:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(others);
                    break;
                default:
                    break;
            }
            groupVos.add(plateTypeGroupVo);
        }
        return groupVos;
    }

    /**
     * 多孔板可选列表
     * 可以调用的范围，同一个实验中，之前阶段使用过的多孔板
     *
     * @param labId
     * @param experimentId
     * @param experimentPhaseId
     * @return
     */
    public List<PlateTypeGroupVo> getPhaseUsablePlateList(Long labId, Long experimentId, Long experimentPhaseId) {

        //系统多孔板类型
        List<Dict> plateTypes = dictService.getPlateType();
        List<PlateTypeVo> plateTypeVos = new ArrayList<>();

        //实验
        assertExperimentExist(experimentId, labId, null);

        //多空板
        List<Plate> plates;
        if(experimentPhaseId!=null){
            //步骤
            ExperimentPhase phase = experimentPhaseService.selectById(experimentPhaseId);
            Assert.isTrue(phase != null && phase.getExperimentId().equals(experimentId), "phaseId 不属于 id 为" + experimentId + "的实验");
            //可见的多孔板
            plates = plateService.selectListByExperimentIdAndLtSort(experimentId, phase.getSort());
        }else {
            plates = plateService.selectListByExperimentId(experimentId);
        }
        //返回结果处理
        List<Integer> types = plates.stream().map(Plate::getType).distinct().collect(toList());
        types.forEach(type -> {
            List<Plate> typePlates = plates.stream().filter(e -> e.getType().equals(type)).collect(toList());
            if (typePlates.size() > 0) {
                for (int i = 0; i < typePlates.size(); i++) {
                    PlateTypeVo plateTypeVo = getPlateTypeVo(plateTypes, typePlates, i);
                    plateTypeVos.add(plateTypeVo);
                }
            } else {
                PlateTypeVo plateTypeVo = getPlateTypeVo(plateTypes, typePlates, 0);
                plateTypeVos.add(plateTypeVo);
            }

        });

        return getPlateTypeGroupVosExist(plateTypeVos);
    }

    private PlateTypeVo getPlateTypeVo(List<Dict> plateTypes, List<Plate> typePlates, int index) {
        Plate tp = typePlates.get(index);
        Integer size = tp.getColumns() * tp.getRows();
        Dict dict = plateTypes.stream().filter(e -> size.equals(e.getCode())).findFirst().orElse(null);
        int code = (dict != null && dict.getType().equals(PlateTypeEnum.getName(tp.getType()))) ? dict.getCode() : 1;
        PlateTypeVo plateTypeVo = new PlateTypeVo();
        plateTypeVo.setId(tp.getId());
        plateTypeVo.setType(tp.getType());
        plateTypeVo.setCode(code);
        plateTypeVo.setRows(tp.getRows());
        plateTypeVo.setColumns(tp.getColumns());
        plateTypeVo.setName(tp.getName());
        return plateTypeVo;
    }

    private List<PlateTypeGroupVo> getPlateTypeGroupVosExist(List<PlateTypeVo> plateTypeVos) {
        //分组
        List<PlateTypeVo> porePlate = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.PORE_PLATE.getValue())).collect(toList());
        List<PlateTypeVo> gumPlate = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())).collect(toList());
        List<PlateTypeVo> cdPlate = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.CULTURE_DISH.getValue())).collect(toList());
        List<PlateTypeVo> cbPlate = plateTypeVos.stream().filter(e -> e.getType().equals(PlateTypeEnum.CULTURE_BOTTLER.getValue())).collect(toList());
        List<PlateTypeGroupVo> groupVos = new ArrayList<>();
        for (int i = 1; i <= 4; i++) {
            PlateTypeGroupVo plateTypeGroupVo = new PlateTypeGroupVo();
            switch (i) {
                case 1:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(porePlate);
                    break;
                case 2:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(gumPlate);
                    break;
                case 3:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(cdPlate);
                    break;
                default:
                    plateTypeGroupVo.setGroup(i);
                    plateTypeGroupVo.setItems(cbPlate);
                    break;
            }
            groupVos.add(plateTypeGroupVo);
        }
        return groupVos;
    }

    /**
     * 多孔板选择
     *
     * @param plateAdd
     * @param labId
     * @param labMemberId
     * @return
     */
    public Plate createPlateOrBindExistPlateToStep(PlateAdd plateAdd, Long labId, Long labMemberId) {

        //实验校验
        assertExperimentExist(plateAdd.getExperimentId(), labId, labMemberId);

        Plate plate = null;
        if (plateAdd.getPlateId() == null) {
            // 实验前/实验中 新建
            plate = this.createPlateAndBindStep(plateAdd.getExperimentId()
                    , plateAdd.getPhaseId(), plateAdd.getStepId()
                    , plateAdd.getTypeId(), plateAdd.getRowNum(), plateAdd.getColumnNum());
        } else {
            //实验中 选择
            plate = this.bindExistPlateToStep(plateAdd.getExperimentId(), plateAdd.getPlateId()
                    , plateAdd.getPhaseId(), plateAdd.getStepId());
        }

        return plate;
    }

    /**
     * 新建多孔板
     *
     * @param experimentId
     * @param phaseId
     * @param stepId
     * @param rowNum
     * @param columnNum
     * @return
     */
    private Plate createPlateAndBindStep(Long experimentId, Long phaseId, Long stepId, Long typeId,
                                         Integer rowNum, Integer columnNum) {

        //查询需要关联的实验阶段和步骤
        ExperimentPhase phase = null;
        ExperimentStep step = null;
        if (phaseId != null && stepId != null) {
            phase = experimentPhaseService.selectById(phaseId);
            Assert.isTrue(phase != null && phase.getExperimentId().equals(experimentId), "实验阶段不存在！");
            step = experimentStepService.selectById(stepId);
            Assert.isTrue(step != null && step.getExperimentId().equals(experimentId) && step.getExperimentPhaseId().equals(phaseId), "实验步骤不存在！");
        }

        //新建plate
        Dict typeObj = dictService.selectById(typeId);
        Plate plate = createPlate(experimentId, PlateTypeEnum.getValue(typeObj.getType()), typeObj.getCode(), rowNum, columnNum, phase);

        //生成grid
        createPlateGrids(plate, columnNum);

        //新建plate_step_relate
        createPlateStepRelate(experimentId, phase, step, plate);

        //开始
        if(step!=null){
            experimentStepService.startStep(step.getId());
        }

        return plate;
    }

    private void createPlateStepRelate(Long experimentId, ExperimentPhase phase, ExperimentStep step, Plate plate) {
        if(phase!=null && step!=null && plate!=null){
            PlateStepRelate relate = new PlateStepRelate();
            relate.setExperimentId(experimentId);
            relate.setPhaseId(phase.getId());
            relate.setPhaseSort(phase.getSort());
            relate.setStepId(step.getId());
            relate.setStepSort(step.getSort());
            relate.setPlateId(plate.getId());
            Assert.isTrue(plateStepRelateService.insert(relate), "业务操作失败！");
        }
    }

    private void createPlateGrids(Plate plate, Integer plusColumnNum) {
        Long plateId = plate.getId();
        Integer columns = plate.getColumns();
        Integer rows = plate.getRows();
        List<PlateGrid> exists = plateGridService.selectList(plateId);

        List<PlateGrid> plateGrids = new ArrayList<>();
        if (exists != null && exists.size() > 0) {
            //培养皿、培养瓶 已存在的情况
            if (plate.getType().equals(PlateTypeEnum.CULTURE_DISH.getValue())
                    || plate.getType().equals(PlateTypeEnum.CULTURE_BOTTLER.getValue())) {
                plusColumnNum = (columns + plusColumnNum) <= 300 ? plusColumnNum : (300 - columns);
                for (int i = 1; i <= plusColumnNum; i++) {
                    PlateGrid plateGrid = new PlateGrid();
                    plateGrid.setPlateId(plateId);
                    plateGrid.setColumnNo(columns + i);
                    plateGrid.setRowNo("1");
                    plateGrids.add(plateGrid);
                }
                if (plate.getType().equals(PlateTypeEnum.CULTURE_DISH.getValue())) {
                    plate.setName(String.format("培养皿*%s", columns + plusColumnNum));
                } else {
                    plate.setName(String.format("培养瓶*%s", columns + plusColumnNum));
                }
                plate.setColumns(columns + plusColumnNum);
                plateService.update(plate);
            }
        } else {
            if (plate.getType().equals(PlateTypeEnum.PORE_PLATE.getValue())) {
                //多孔板
                for (int i = 1; i <= columns; i++) {
                    for (int j = 'A'; j <= 'A' + (rows - 1); j++) {
                        PlateGrid plateGrid = new PlateGrid();
                        plateGrid.setPlateId(plateId);
                        plateGrid.setColumnNo(i);
                        plateGrid.setRowNo(String.valueOf((char) j));
                        plateGrids.add(plateGrid);
                    }
                }
            } else {
                //胶板、培养皿、培养瓶使用
                for (int i = 1; i <= columns; i++) {
                    PlateGrid plateGrid = new PlateGrid();
                    plateGrid.setPlateId(plateId);
                    plateGrid.setColumnNo(i);
                    plateGrid.setRowNo("1");
                    plateGrids.add(plateGrid);
                }
            }
        }

        if (plateGrids.size() > 0) {
            plateGridService.insertBatch(plateGrids);
        }
    }

    private Plate createPlate(Long experimentId, Integer type, Integer code, Integer rowNum, Integer columnNum, ExperimentPhase phase) {

        //已存在培养皿 培养瓶
        List<Plate> exists = plateService.selectListByExperimentIdAndType(experimentId, type);

        Plate plate = new Plate();
        plate.setExperimentId(experimentId);
        if(phase!=null){
            plate.setPhaseSort(phase.getSort());
            plate.setPhaseId(phase.getId());
        }
        plate.setType(type);
        if (type.equals(PlateTypeEnum.PORE_PLATE.getValue())) {
            if (code.equals(PlateTypeCodeConstant.PORE6) || (code.equals(PlateTypeCodeConstant.ONE) && Integer.valueOf(3).equals(columnNum) && Integer.valueOf(2).equals(rowNum))) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(2).equals(e.getRows()) && Integer.valueOf(3).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("6孔板(%s)", exists.size()));
                    } else {
                        plate.setName("6孔板");
                    }
                }
                plate.setColumns(3);
                plate.setRows(2);
            } else if (code.equals(PlateTypeCodeConstant.PORE12) || (code.equals(PlateTypeCodeConstant.ONE) && Integer.valueOf(4).equals(columnNum) && Integer.valueOf(3).equals(rowNum))) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(3).equals(e.getRows()) && Integer.valueOf(4).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("12孔板(%s)", exists.size()));
                    } else {
                        plate.setName("12孔板");
                    }
                }
                plate.setColumns(4);
                plate.setRows(3);
            } else if (code.equals(PlateTypeCodeConstant.PORE24) || (code.equals(PlateTypeCodeConstant.ONE) && Integer.valueOf(6).equals(columnNum) && Integer.valueOf(4).equals(rowNum))) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(4).equals(e.getRows()) && Integer.valueOf(6).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("24孔板(%s)", exists.size()));
                    } else {
                        plate.setName("24孔板");
                    }
                }
                plate.setColumns(6);
                plate.setRows(4);
            } else if (code.equals(PlateTypeCodeConstant.PORE48) || (code.equals(PlateTypeCodeConstant.ONE) && Integer.valueOf(8).equals(columnNum) && Integer.valueOf(6).equals(rowNum))) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(6).equals(e.getRows()) && Integer.valueOf(8).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("48孔板(%s)", exists.size()));
                    } else {
                        plate.setName("48孔板");
                    }
                }
                plate.setColumns(8);
                plate.setRows(6);
            } else if (code.equals(PlateTypeCodeConstant.PORE96) || (code.equals(PlateTypeCodeConstant.ONE) && Integer.valueOf(12).equals(columnNum) && Integer.valueOf(8).equals(rowNum))) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(8).equals(e.getRows()) && Integer.valueOf(12).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("96孔板(%s)", exists.size()));
                    } else {
                        plate.setName("96孔板");
                    }
                }
                plate.setColumns(12);
                plate.setRows(8);
            } else if (code.equals(PlateTypeCodeConstant.PORE384) || (code.equals(PlateTypeCodeConstant.ONE) && Integer.valueOf(24).equals(columnNum) && Integer.valueOf(16).equals(rowNum))) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(16).equals(e.getRows()) && Integer.valueOf(24).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("384孔板(%s)", exists.size()));
                    } else {
                        plate.setName("384孔板");
                    }
                }
                plate.setColumns(24);
                plate.setRows(16);
            } else if (code.equals(PlateTypeCodeConstant.ONE)) {
                Assert.isTrue(columnNum != null && rowNum != null, "columnNum and rowNum can not be null!");
                if (exists != null && exists.size() > 0) {
                    plate.setName(String.format("自定义多孔板*%s", columnNum * rowNum));
                } else {
                    plate.setName("自定义多孔板");
                }
                plate.setColumns(columnNum);
                plate.setRows(rowNum);
            } else {
                throw new BrillilabException("类型不存在!");
            }
        } else if (type.equals(PlateTypeEnum.GUM_PLATE.getValue())) {
            if (code.equals(PlateTypeCodeConstant.GUM10)) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(1).equals(e.getRows()) && Integer.valueOf(10).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("10胶板(%s)", exists.size()));
                    } else {
                        plate.setName("10胶板");
                    }
                }
                plate.setColumns(10);
                plate.setRows(1);
            } else if (code.equals(PlateTypeCodeConstant.GUM15)) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(1).equals(e.getRows()) && Integer.valueOf(15).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("15胶板(%s)", exists.size()));
                    } else {
                        plate.setName("15胶板");
                    }
                }
                plate.setColumns(15);
                plate.setRows(1);
            } else if (code.equals(PlateTypeCodeConstant.GUM20)) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(1).equals(e.getRows()) && Integer.valueOf(20).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("20胶板(%s)", exists.size()));
                    } else {
                        plate.setName("20胶板");
                    }
                }
                plate.setColumns(20);
                plate.setRows(1);
            } else if (code.equals(PlateTypeCodeConstant.GUM30)) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(1).equals(e.getRows()) && Integer.valueOf(30).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("30胶板(%s)", exists.size()));
                    } else {
                        plate.setName("30胶板");
                    }
                }
                plate.setColumns(30);
                plate.setRows(1);
            } else if (code.equals(PlateTypeCodeConstant.GUM40)) {
                if (exists != null) {
                    exists = exists.stream().filter(e -> Integer.valueOf(1).equals(e.getRows()) && Integer.valueOf(40).equals(e.getColumns())).collect(toList());
                    if (exists.size() > 0) {
                        plate.setName(String.format("40胶板(%s)", exists.size()));
                    } else {
                        plate.setName("40胶板");
                    }
                }
                plate.setColumns(40);
                plate.setRows(1);
            } else {
                throw new BrillilabException("类型不存在!");
            }
        } else if (type.equals(PlateTypeEnum.CULTURE_DISH.getValue())
                || type.equals(PlateTypeEnum.CULTURE_BOTTLER.getValue())) {
            Assert.notNull(columnNum, "columnNum can not be null!");

            if (CollectionUtils.isNotEmpty(exists)) {
                return exists.stream().findFirst().orElse(null);
            }

            if (type.equals(PlateTypeEnum.CULTURE_DISH.getValue())) {
                plate.setName(String.format("培养皿*%s", columnNum));
            } else {
                plate.setName(String.format("培养瓶*%s", columnNum));
            }

            plate.setColumns(columnNum);
            plate.setRows(1);
        } else {
            throw new BrillilabException("类型不存在!");
        }

        plateService.insert(plate);

        return plate;
    }

    /**
     * 选择关联多孔板
     *
     * @param experimentId
     * @param phaseId
     * @param stepId
     * @return
     */
    private Plate bindExistPlateToStep(Long experimentId, Long plateId, Long phaseId, Long stepId) {

        ExperimentPhase phase = experimentPhaseService.selectById(phaseId);
        Assert.isTrue(phase != null && phase.getExperimentId().equals(experimentId), "实验阶段不存在！");
        ExperimentStep step = experimentStepService.selectById(stepId);
        Assert.isTrue(step != null && step.getExperimentId().equals(experimentId) && step.getExperimentPhaseId().equals(phaseId), "实验步骤不存在！");

        Plate plate = plateService.selectById(plateId);
        Assert.notNull(plate, "多孔板/胶板/培养皿/培养瓶不存在！");
        Assert.isTrue(plate.getExperimentId().equals(experimentId), "多孔板/胶板/培养皿/培养瓶不属于该实验！");

        List<PlateStepRelate> existRelate = plateStepRelateService.selectList(experimentId, plateId, phaseId, stepId);
        Assert.isTrue(existRelate.size() == 0, "多孔板/胶板/培养皿/培养瓶已关联过该步骤！");

        //新建plate_step_relate
        createPlateStepRelate(experimentId, phase, step, plate);

//        List<PlateStepRelate> existRelateCheck=plateStepRelateService.selectList(experimentId,plateId,phaseId,stepId);
//        Assert.isTrue(existRelateCheck.size() == 1 || existRelateCheck.size() == 0 ,"多孔板/胶板/培养皿/培养瓶已关联过该步骤！");

        return plate;
    }

    /**
     * 多孔板删除
     *
     * @param experimentId
     * @param plateId
     * @param labId
     * @param labMemberId
     * @return
     */
    public void deletePlate(Long experimentId, Long plateId, Long labId, Long labMemberId) {

        //实验
        assertExperimentExist(experimentId, labId, labMemberId);

        //多孔板
        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        //删除多孔板
        plate.setIsDelete(BoolEnum.TRUE.getValue());
        plateService.update(plate);

        //删除多孔板的孔
        plateGridService.deleteByPlateId(plateId);

        //删除多孔板与实验步骤的关联
        plateStepRelateService.deleteByPlateId(plateId);

        //删除多孔板的孔中的试剂/样本/时间/文本
        plateGridReagentService.deleteByPlateId(plateId);
        plateGridTimerService.deleteByPlateId(plateId);
        plateGridTextService.deleteByPlateId(plateId);
        plateGridGroupService.deleteByPlateId(plateId);
    }

    /**
     * 获取单块多孔板信息
     *
     * @param experimentId
     * @param plateId
     * @param labId
     * @return
     */
    public PlateVo getPlateInfo(Long experimentId,Long plateId,Long labId) {

        //实验
        assertExperimentExist(experimentId, labId, null);

        //多孔板
        Plate plate = assertPlateExistAndGet(experimentId, plateId);
        PlateVo plateVo = getPlateVo(plate);

        int tc = plateGridTextService.selectCountOnePlate(plateId);
        int rc = plateGridReagentService.selectCountOnePlate(plateId);
        int tmc = plateGridTimerService.selectCountOnePlate(plateId);
        int gc = plateGridGroupService.selectCountOnePlate(plateId);

        //孔
        List<PlateGrid> plateGrids = plateGridService.selectList(plateId);
        List<PlateGridVo> plateGridVos = plateGrids.stream().map(e -> {
            PlateGridVo plateGridVo = new PlateGridVo();
            BeanUtils.copyProperties(e, plateGridVo);
            setContentTypeToPlateGridVp(tc, rc, tmc,gc, plateGridVo);
            return plateGridVo;
        }).collect(toList());
        plateVo.setGrids(plateGridVos);

        //contentType
        setContentTypeToPlateVo(plateVo, tc, rc, tmc,gc);

        if (plate.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())) {
            List<PlateGridTimer> timers = plateGridTimerService.selectList(plateId);
            plateVo.setPlateTimer(timers);
        }

        return plateVo;
    }

    private void setContentTypeToPlateVo(PlateVo plateVo, int tc, int rc, int tmc,int gc) {
        if (tc != 0 && rc == 0 && gc == 0) {
            plateVo.setContentType(PlateContentTypeEnum.TEXT.getValue());
        } else if (rc != 0 && tc == 0 && gc == 0) {
            plateVo.setContentType(PlateContentTypeEnum.SAMPLE_REAGENT.getValue());
        } else if (tmc != 0 && tc == 0 && rc == 0 && gc == 0) {
            plateVo.setContentType(PlateContentTypeEnum.TIMER.getValue());
        } else if (tc == 0 && rc == 0 && tc == 0 && gc!=0) {
            plateVo.setContentType(PlateContentTypeEnum.GROUP.getValue());
        } else if (tc == 0 && rc == 0 && tc == 0 && gc == 0 ){
            plateVo.setContentType(PlateContentTypeEnum.EMPTY.getValue());
        }
    }

    private void setContentTypeToPlateGridVp(int tc, int rc, int tmc,int gc, PlateGridVo plateGridVo) {
        if (tc != 0 && rc == 0 && gc == 0) {
            plateGridVo.setContentType(PlateContentTypeEnum.TEXT.getValue());
        } else if (rc != 0 && tc == 0 && gc == 0) {
            plateGridVo.setContentType(PlateContentTypeEnum.SAMPLE_REAGENT.getValue());
        } else if (tmc != 0 && tc == 0 && rc == 0 && gc == 0) {
            plateGridVo.setContentType(PlateContentTypeEnum.TIMER.getValue());
        } else if (tc == 0 && rc == 0 && tc == 0 && gc!=0) {
            plateGridVo.setContentType(PlateContentTypeEnum.GROUP.getValue());
        } else if (tc == 0 && rc == 0 && tc == 0 && gc == 0 ){
            plateGridVo.setContentType(PlateContentTypeEnum.EMPTY.getValue());
        }
    }

    private PlateVo getPlateVo(Plate plate) {
        PlateVo plateVo = new PlateVo();
        BeanUtils.copyProperties(plate, plateVo);
        return plateVo;
    }

    /**
     * 获取单块多孔板预览
     *
     * @param experimentId
     * @param plateId
     * @param labId
     * @return
     */
    public PlateVo getPlatePreview(Long experimentId, Long plateId, Long labId) {

        //实验
        assertExperimentExist(experimentId, labId, null);

        //多孔板
        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        int plateSize = plate.getColumns() * plate.getRows();

        //plateVo
        PlateVo plateVo = getPlateVo(plate);

        //孔
        List<PlateGrid> plateGrids = plateGridService.selectUsedGridList(plateId);
        List<PlateGridVo> plateGridVos = plateGrids.stream().map(e -> {
            PlateGridVo plateGridVo = new PlateGridVo();
            BeanUtils.copyProperties(e, plateGridVo);
            return plateGridVo;
        }).collect(toList());

        //contents
        List<PlateGridReagent> all = plateGridReagentService.selectList(plateId);
        List<PlateGridReagent> reagentList = all.stream().filter(e -> e.getClassify().equals(KitEnum.Classify.REAGENT.getValue())).collect(toList());
        List<PlateGridReagent> sampleList = all.stream().filter(e -> e.getClassify().equals(KitEnum.Classify.SAMPLE.getValue())).collect(toList());
        List<PlateGridText> textList = plateGridTextService.selectList(plateId);
        List<PlateGridTimer> timerList = plateGridTimerService.selectList(plateId);
        plateGridVos.forEach(e -> {
            List reagents = null;
            List samples = null;
            if (plateSize <= 12) {
                //6、12孔 alias+amount all
                reagents = reagentList.stream().filter(r -> r.getPlateGridId().equals(e.getId())).map(this::getPlateGridReagentVo).collect(toList());
                samples = sampleList.stream().filter(r -> r.getPlateGridId().equals(e.getId())).map(this::getPlateGridReagentVo).collect(toList());
            } else if (plateSize <= 48) {
                //24、48孔 alias all
                reagents = reagentList.stream().filter(r -> r.getPlateGridId().equals(e.getId())).map(PlateGridReagent::getAlias).collect(toList());
                samples = sampleList.stream().filter(r -> r.getPlateGridId().equals(e.getId())).map(PlateGridReagent::getAlias).collect(toList());
            } else {
                reagents = reagentList.stream().filter(r -> r.getPlateGridId().equals(e.getId())).map(PlateGridReagent::getAlias).collect(toList());
                samples = sampleList.stream().filter(r -> r.getPlateGridId().equals(e.getId())).map(PlateGridReagent::getAlias).collect(toList());
            }
            e.setReagents(CollectionUtils.isEmpty(reagents) ? new ArrayList(0) : reagents);
            e.setSamples(CollectionUtils.isEmpty(samples) ? new ArrayList(0) : samples);
            List<String> texts = textList.stream().filter(t -> t.getPlateGridId().equals(e.getId())).map(PlateGridText::getContent).collect(toList());
            e.setTexts(texts);
            if (!plate.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())) {
                List<Long> times = timerList.stream().filter(t -> t.getPlateGridId().equals(e.getId())).map(PlateGridTimer::getTime).collect(toList());
                e.setTimer(times);
            }
        });

        plateVo.setGrids(plateGridVos);

        return plateVo;
    }

    private PlateGridReagentVo getPlateGridReagentVo(PlateGridReagent r) {
        PlateGridReagentVo plateGridReagentVo = new PlateGridReagentVo();
        plateGridReagentVo.setAlias(r.getAlias());
        plateGridReagentVo.setClassify(r.getClassify());
        plateGridReagentVo.setAmount(r.getAmount());
        plateGridReagentVo.setUnit(r.getUnit());
        return plateGridReagentVo;
    }

    /**
     * 多孔板列表（实验中步骤的）
     *
     * @param experimentId
     * @param phaseId
     * @param stepId
     * @param labId
     * @return
     */
    public List<PlateVo> getStepPlateList(Long experimentId, Long phaseId, Long stepId, Long labId) {

        //实验
        assertExperimentExist(experimentId, labId, null);

        List<Plate> plateList;

        if(phaseId!=null && stepId!=null){
            ExperimentStep step = experimentStepService.selectById(stepId);
            Assert.notNull(step, "ID为:" + stepId + "的步骤不存在！");
            Assert.isTrue(step.getExperimentPhaseId().equals(phaseId), "ID为:" + stepId + "的步骤不属于当前阶段！");
            Assert.isTrue(step.getExperimentId().equals(experimentId), "ID为:" + stepId + "的步骤不属于当前实验！");

            //多孔板步骤关系
            List<PlateStepRelate> relates = plateStepRelateService.selectByExperimentIdAndPhaseIdAndGtStepSort(experimentId, phaseId, step.getSort());
            List<Long> plateIds = relates.stream().map(PlateStepRelate::getPlateId).collect(toList());

            //多孔板列表
            plateList = plateService.selectListByIds(plateIds);
        }else{
            plateList = plateService.selectListByExperimentId(experimentId);
        }

        return plateList.stream().map(this::getPlateVo).collect(toList());
    }

    /**
     * 多孔板添加计时器
     *
     * @param plateTimerAdd
     * @param labId
     * @param labMemberId
     * @return
     */
    public void addTimerToPlateGrid(PlateTimerAdd plateTimerAdd, Long labId, Long labMemberId) {

        Long experimentId = plateTimerAdd.getExperimentId();
        Long phaseId = plateTimerAdd.getPhaseId();
        Long stepId = plateTimerAdd.getStepId();
        List<Long> gridIds = plateTimerAdd.getGridIds();
        if (gridIds != null) {
            gridIds = gridIds.stream().distinct().collect(toList());
        }
        Long plateId = plateTimerAdd.getPlateId();
        Long timerId = plateTimerAdd.getTimerId();
        if (phaseId == null || stepId == null) {
            ExperimentPhase phase = experimentPhaseService.selectExperimentFirstPhase(experimentId);
            ExperimentStep step = experimentStepService.selectExperimentFirstStep(experimentId, phase.getId());
            phaseId = phase.getId();
            stepId = step.getId();
        }

        //实验相关校验
        experimentStepService.getExperimentStepAndAssertExist(experimentId, phaseId, stepId, labId, labMemberId);

        //多孔板相关校验
        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        //步骤时间
        ExperimentStepTimer stepTimer = getExperimentStepTimer(timerId, stepId);

        List<PlateGrid> update = new ArrayList<>();
        List<PlateGridTimer> insert = new ArrayList<>();
        if (!plate.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())) {

            Assert.isTrue(gridIds != null && gridIds.size() > 0, "gridIds can not be empty!");

            List<PlateGrid> grids = plateGridService.selectList(gridIds).stream().filter(e -> e.getPlateId().equals(plateId)).collect(toList());
            Assert.isTrue(gridIds.size() == grids.size(), "exist grid not belong to plate !");

            //所有GridTimer
            List<PlateGridTimer> timers = plateGridTimerService.selectList(gridIds);

            for (PlateGrid grid : grids) {

                //*重复校验
                PlateGridTimer exist = timers.stream().filter(e -> e.getPlateGridId().equals(grid.getId()) && e.getStepTimerId().equals(stepTimer.getId())).findFirst().orElse(null);
                if (exist == null) {
                    //新建多孔板时间
                    PlateGridTimer gridTimer = new PlateGridTimer();
                    gridTimer.setPlateId(plateId);
                    //胶板不关联孔
                    gridTimer.setPlateGridId(grid.getId());
                    gridTimer.setStepTimerId(timerId);
                    gridTimer.setTime(stepTimer.getTime());
                    insert.add(gridTimer);

                    if (grid.getIsUsed().equals(BoolEnum.FALSE.getValue())) {
                        grid.setIsUsed(BoolEnum.TRUE.getValue());
                        update.add(grid);
                    }
                }
            }
        } else {
            Assert.isTrue(gridIds == null, "gridIds don't need!");

            //所有GridTimer
            List<PlateGridTimer> timers = plateGridTimerService.selectList(plateId);
            //*重复校验
            PlateGridTimer exist = timers.stream().filter(e -> e.getPlateId().equals(plate.getId()) && e.getStepTimerId().equals(stepTimer.getId())).findFirst().orElse(null);
            if (exist == null) {
                //新建多孔板时间
                PlateGridTimer gridTimer = new PlateGridTimer();
                gridTimer.setPlateId(plateId);
                //胶板不关联孔
                gridTimer.setStepTimerId(timerId);
                gridTimer.setTime(stepTimer.getTime());
                insert.add(gridTimer);
            }
        }

        if (insert.size() > 0) {
            plateGridTimerService.insertBatch(insert);
        }

        if (update.size() > 0) {
            plateGridService.updateBatch(update);
        }

    }

    private ExperimentStepTimer getExperimentStepTimer(Long timerId, Long stepId) {
        ExperimentStepTimer stepTimer = experimentStepTimerService.selectById(timerId);
        Assert.notNull(stepTimer, "步骤时间不存在！");
        Assert.isTrue(stepTimer.getStepId().equals(stepId), "步骤时间不属于该步骤！");
        return stepTimer;
    }

    private void assertCanAddNewContent(Plate plate, PlateGrid grid, boolean isReagent) {
        if (plate.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())) {
            if (grid.getIsUsed().equals(BoolEnum.TRUE.getValue())) {
                if (isReagent) {
                    throw new BrillilabException(ResultEnum.DATA_ALREADY_EXIST, "已加样，是否覆盖？");
                } else {
                    throw new BrillilabException(ResultEnum.DATA_ALREADY_EXIST, "已加文本，是否覆盖？");
                }
            }
        }
    }

    /**
     * 多孔板添加试剂/样品
     *
     * @param plateReagentAdd
     * @param labId
     * @param labMemberId
     * @return
     */
    public void addMaterialToPlateGrid(PlateReagentAdd plateReagentAdd, Long labId, Long labMemberId) {

        Map<Integer, String> unitMap = dictService.getUnitMap();

        Long experimentId = plateReagentAdd.getExperimentId();
        Long phaseId = plateReagentAdd.getPhaseId();
        Long stepId = plateReagentAdd.getStepId();
        List<Long> gridIds = plateReagentAdd.getGridIds().stream().distinct().collect(toList());
        Long plateId = plateReagentAdd.getPlateId();
        Long sourceId = plateReagentAdd.getStepReagentId();
        Integer type = plateReagentAdd.getType();
        BigDecimal amount = plateReagentAdd.getAmount();
//        if (phaseId == null || stepId == null) {
//            ExperimentPhase phase = experimentPhaseService.selectExperimentFirstPhase(experimentId);
//            ExperimentStep step = experimentStepService.selectExperimentFirstStep(experimentId, phase.getId());
//            phaseId = phase.getId();
//            stepId = step.getId();
//        }

        //实验相关校验
//        experimentStepService.getExperimentStepAndAssertExist(experimentId, phaseId, stepId, labId, labMemberId);

        //多孔板相关校验
        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        List<PlateGrid> grids = plateGridService.selectList(gridIds).stream().filter(e -> e.getPlateId().equals(plateId)).collect(toList());
        Assert.isTrue(gridIds.size() == grids.size(), "exist grid not belong to plate !");

        //*校验是否有文本 整块多孔板只能全部是文本或全部是试剂、样品、时间
        assertCanAddTimerOrReagent(plateId);

        //步骤试剂 实验试剂
        ExperimentStepReagent stepReagent = null;
        ExperimentBill experimentBill = null;
        if (plateReagentAdd.getType().equals(ExperimentStepMaterialTypeEnum.STEP_REAGENT.getType())) {
            stepReagent = experimentStepReagentService.selectById(plateReagentAdd.getStepReagentId());
            Assert.notNull(stepReagent, "试剂不存在！");
            Assert.isTrue(stepReagent.getStepId().equals(plateReagentAdd.getStepId()), "试剂不属于该实验步骤！");
        } else if (plateReagentAdd.getType().equals(ExperimentStepMaterialTypeEnum.EXPERIMENT_SAMPLE.getType())) {
            experimentBill = experimentBillService.selectById(plateReagentAdd.getStepReagentId());
            Assert.notNull(experimentBill, "样品不存在");
            Assert.isTrue(experimentBill.getExperimentId().equals(plateReagentAdd.getExperimentId()), "样品不属于该实验！");
        } else {
            throw new BrillilabException("类型:" + plateReagentAdd.getType() + "不存在！");
        }

        //所有reagent
        List<PlateGridReagent> reagents = plateGridReagentService.selectList(gridIds);

        //逻辑
        List<PlateGrid> update = new ArrayList<>();
        List<PlateGridReagent> insertRe = new ArrayList<>();
        List<PlateGridReagent> updateRe = new ArrayList<>();
        for (PlateGrid grid : grids) {
            //*胶板只能添加一个
            assertCanAddNewContent(plate, grid, Boolean.TRUE);

            PlateGridReagent exist = reagents.stream().filter(e -> e.getPlateGridId().equals(grid.getId()) && e.getSourceId().equals(sourceId) && e.getType().equals(type)).findFirst().orElse(null);

            if (exist == null) {
                //插入
                //experiment_step_reagent中的试剂/样品
                PlateGridReagent gridReagent = new PlateGridReagent();
                if (plateReagentAdd.getType().equals(ExperimentStepMaterialTypeEnum.STEP_REAGENT.getType())) {
                    gridReagent.setPlateId(plateReagentAdd.getPlateId());
                    gridReagent.setPlateGridId(grid.getId());
                    gridReagent.setFirstReagentTypeId(stepReagent.getFirstReagentTypeId());
                    gridReagent.setSourceId(stepReagent.getId());
                    gridReagent.setType(ExperimentStepMaterialTypeEnum.STEP_REAGENT.getType());
                    gridReagent.setClassify(stepReagent.getClassify());
                    gridReagent.setName(stepReagent.getName());
                    gridReagent.setAlias(stepReagent.getAlias());
                    gridReagent.setUnit(stepReagent.getUnit());
                    gridReagent.setAmount(plateReagentAdd.getAmount());

                    //experiment_bill中的样品
                } else if (plateReagentAdd.getType().equals(ExperimentStepMaterialTypeEnum.EXPERIMENT_SAMPLE.getType())) {
                    gridReagent.setPlateId(plateReagentAdd.getPlateId());
                    gridReagent.setPlateGridId(grid.getId());
                    gridReagent.setFirstReagentTypeId(experimentBill.getFirstReagentTypeId());
                    gridReagent.setSourceId(experimentBill.getId());
                    gridReagent.setType(ExperimentStepMaterialTypeEnum.EXPERIMENT_SAMPLE.getType());
                    gridReagent.setClassify(experimentBill.getClassify());
                    gridReagent.setName(experimentBill.getName());
                    gridReagent.setAlias(experimentBill.getAlias());
                    gridReagent.setUnit(unitMap.get(experimentBill.getUnit()));
                    gridReagent.setAmount(plateReagentAdd.getAmount());

                } else {
                    throw new BrillilabException(ResultEnum.DATA_NOT_EXIST, "type does not exist!");
                }
                insertRe.add(gridReagent);
            } else {
                //重复量覆盖
                if (amount != null) {
                    exist.setAmount(amount.compareTo(BigDecimal.ZERO) >= 0 ? amount : BigDecimal.ZERO);
                } else {
                    exist.setAmount(amount);
                }
                updateRe.add(exist);
            }

            if (grid.getIsUsed().equals(BoolEnum.FALSE.getValue())) {
                grid.setIsUsed(BoolEnum.TRUE.getValue());
                update.add(grid);
            }


        }

        if (insertRe.size() > 0) {
            plateGridReagentService.insertBatch(insertRe);
        }

        if (updateRe.size() > 0) {
            plateGridReagentService.updateBatch(updateRe);
        }

        if (update.size() > 0) {
            plateGridService.updateBatch(update);
        }

    }

    private void assertCanAddTimerOrReagent(Long plateId) {
        int textCount = plateGridTextService.selectCountOnePlate(plateId);
        int groupCount = plateGridGroupService.selectCountOnePlate(plateId);
        Assert.isTrue(textCount == 0 && groupCount == 0, "已存在文本/实验分组，不可加入试剂/样品/时间！");
    }

    /**
     * 多孔板添加文本
     *
     * @param plateTextAdd
     * @param labId
     * @param labMemberId
     * @return
     */
    public void addTextToPlateGrid(PlateTextAdd plateTextAdd, Long labId, Long labMemberId) {

        Long experimentId = plateTextAdd.getExperimentId();
        Long phaseId = plateTextAdd.getPhaseId();
        Long stepId = plateTextAdd.getStepId();
        Long stepTextId = plateTextAdd.getStepTextId();
        Long plateId = plateTextAdd.getPlateId();
        List<Long> gridIds = plateTextAdd.getGridIds();
//        if (phaseId == null || stepId == null) {
//            ExperimentPhase phase = experimentPhaseService.selectExperimentFirstPhase(experimentId);
//            ExperimentStep step = experimentStepService.selectExperimentFirstStep(experimentId, phase.getId());
//            phaseId = phase.getId();
//            stepId = step.getId();
//        }

        //实验相关校验
//        experimentStepService.getExperimentStepAndAssertExist(experimentId, phaseId, stepId, labId, labMemberId);

        //多孔板相关校验
        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        //grids
        List<PlateGrid> grids = plateGridService.selectList(gridIds).stream().filter(e -> e.getPlateId().equals(plateId)).collect(toList());
        Assert.isTrue(gridIds.size() == grids.size(), "exist grid not belong to plate !");

        //步骤文本
        ExperimentStepText stepText = experimentStepTextService.selectById(stepTextId);
        Assert.notNull(stepText, "文本不存在！");
//        Assert.isTrue(stepText.getStepId().equals(plateTextAdd.getStepId()), "文本不属于该步骤！");

        //校验是否有试剂/样品/计时器
        assertCanAddNewText(plate);

        //gridtexts
        List<PlateGridText> texts = plateGridTextService.selectList(gridIds);

        //逻辑
        List<PlateGrid> update = new ArrayList<>();
        List<PlateGridText> insert = new ArrayList<>();
        for (PlateGrid grid : grids) {

            //胶板只能添加一个
            assertCanAddNewContent(plate, grid, Boolean.FALSE);

            //重复校验
            PlateGridText exist = texts.stream().filter(e -> e.getStepTextId().equals(stepTextId) && e.getPlateGridId().equals(grid.getId())).findFirst().orElse(null);

            if (exist == null) {
                //插入
                PlateGridText gridText = new PlateGridText();
                gridText.setPlateId(plateId);
                gridText.setPlateGridId(grid.getId());
                gridText.setStepTextId(stepText.getId());
                gridText.setContent(stepText.getContent());
                insert.add(gridText);
            }

            if (grid.getIsUsed().equals(BoolEnum.FALSE.getValue())) {
                grid.setIsUsed(BoolEnum.TRUE.getValue());
                update.add(grid);
            }
        }

        if (insert.size() > 0) {
            plateGridTextService.insertBatch(insert);
        }

        if (update.size() > 0) {
            plateGridService.updateBatch(update);
        }
    }

    private void assertCanAddNewText(Plate plate) {
        int reagentCount = plateGridReagentService.selectCountOnePlate(plate.getId());
        int groupCount=plateGridGroupService.selectCountOnePlate(plate.getId());
        Assert.isTrue(reagentCount == 0 && groupCount == 0, "已存在试剂/样品/实验分组，不可加入文本！");
    }

    /**
     * 获取多孔板栅格详情 样品/试剂/时间/文本
     *
     * @param experimentId
     * @param plateId
     * @param gridId
     * @param labId
     * @return
     */
    public PlateGridDetailVo getPlateGridDetail(Long experimentId, Long plateId, Long gridId, Long labId) {

        assertExperimentExist(experimentId, labId, null);

        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        PlateGrid grid = assertPlateGridExistAndGet(plateId, gridId);

        PlateGridDetailVo plateGridDetailVo = new PlateGridDetailVo();
        BeanUtils.copyProperties(grid, plateGridDetailVo);

        List<PlateGridReagentVo> reagentList = plateGridReagentService.selectVoList(plateId, gridId, KitEnum.Classify.REAGENT.getValue());
        List<PlateGridReagentVo> sampleList = plateGridReagentService.selectVoList(plateId, gridId, KitEnum.Classify.SAMPLE.getValue());
        List<PlateGridTextVo> textList = plateGridTextService.selectVoList(plateId, gridId);
        List<PlateTimerVo> timerList = plateGridTimerService.selectVoList(plateId, gridId);

        plateGridDetailVo.setReagents(reagentList);
        plateGridDetailVo.setSamples(sampleList);
        plateGridDetailVo.setTexts(textList);
        plateGridDetailVo.setTimers(timerList);

        int tc = plateGridTextService.selectCountOnePlate(plateId);
        int rc = plateGridReagentService.selectCountOnePlate(plateId);
        int tmc = plateGridTimerService.selectCountOnePlate(plateId);
        int gc=plateGridGroupService.selectCountOnePlate(plateId);
        setContentTypeToPlateGridDetailVo(plateGridDetailVo, tc, rc, tmc,gc);

        return plateGridDetailVo;
    }

    /**
     * 获取多孔板栅格详情 样品/试剂/时间/文本
     *
     * @param experimentId
     * @param plateId
     * @param gridIds
     * @param labId
     * @return
     */
    public List<PlateGridDetailVo> getPlateGridDetail(Long experimentId, Long plateId, List<Long> gridIds, Long labId) {

        assertExperimentExist(experimentId, labId, null);

        Plate plate = assertPlateExistAndGet(experimentId, plateId);

        List<PlateGrid> plateGrids = plateGridService.selectList(gridIds);
        List<PlateGridReagentVo> reagents = plateGridReagentService.selectVoList(gridIds);
        List<PlateGridTimer> timers = plateGridTimerService.selectList(gridIds);
        List<PlateGridText> texts = plateGridTextService.selectList(gridIds);
        List<PlateGridGroupVo> groups=plateGridGroupService.selectVoList(gridIds);

        int tc = plateGridTextService.selectCountOnePlate(plateId);
        int rc = plateGridReagentService.selectCountOnePlate(plateId);
        int tmc = plateGridTimerService.selectCountOnePlate(plateId);
        int gc=plateGridGroupService.selectCountOnePlate(plateId);

        List<PlateGridDetailVo> plateGridDetailVos = gridIds.stream().map(gridId -> {

            PlateGrid grid = plateGrids.stream().filter(e -> e.getId().equals(gridId)).findFirst().orElse(null);

            if (grid != null) {
                PlateGridDetailVo plateGridDetailVo = new PlateGridDetailVo();
                BeanUtils.copyProperties(grid, plateGridDetailVo);

                List<PlateGridReagentVo> reagentList = reagents.stream().filter(e -> e.getPlateGridId().equals(gridId) && e.getClassify().equals(KitEnum.Classify.REAGENT.getValue())).collect(toList());
                List<PlateGridReagentVo> sampleList = reagents.stream().filter(e -> e.getPlateGridId().equals(gridId) && e.getClassify().equals(KitEnum.Classify.SAMPLE.getValue())).collect(toList());
                List<PlateGridTextVo> textList = texts.stream().filter(e -> e.getPlateGridId().equals(gridId)).map(e -> MyBeanUtils.copyBean(e, PlateGridTextVo.class)).collect(toList());
                PlateGridGroupVo group=groups.stream().filter(e -> e.getPlateGridId().equals(gridId)).findFirst().orElse(null);

                plateGridDetailVo.setReagents(reagentList);
                plateGridDetailVo.setSamples(sampleList);
                plateGridDetailVo.setTexts(textList);
                plateGridDetailVo.setGroup(group);

                if (!PlateTypeEnum.GUM_PLATE.getValue().equals(plate.getType())) {
                    List<PlateTimerVo> timerList = timers.stream().filter(e -> e.getPlateGridId().equals(gridId)).map(e -> MyBeanUtils.copyBean(e, PlateTimerVo.class)).collect(toList());
                    plateGridDetailVo.setTimers(timerList);
                } else {
                    plateGridDetailVo.setTimers(new ArrayList<>(0));
                }

                setContentTypeToPlateGridDetailVo(plateGridDetailVo, tc, rc, tmc,gc);
                return plateGridDetailVo;
            }
            return null;
        }).filter(Objects::nonNull).collect(toList());

        return plateGridDetailVos;
    }

    private void setContentTypeToPlateGridDetailVo(PlateGridDetailVo plateGridDetailVo, int tc, int rc, int tmc,int gc) {
        if (tc != 0 && rc == 0 && gc == 0) {
            plateGridDetailVo.setContentType(PlateContentTypeEnum.TEXT.getValue());
        } else if (rc != 0 && tc == 0 && gc == 0) {
            plateGridDetailVo.setContentType(PlateContentTypeEnum.SAMPLE_REAGENT.getValue());
        } else if (tmc != 0 && tc == 0 && rc == 0 && gc == 0) {
            plateGridDetailVo.setContentType(PlateContentTypeEnum.TIMER.getValue());
        } else if (tc == 0 && rc == 0 && tc == 0 && gc!=0) {
            plateGridDetailVo.setContentType(PlateContentTypeEnum.GROUP.getValue());
        } else if (tc == 0 && rc == 0 && tc == 0 && gc == 0 ){
            plateGridDetailVo.setContentType(PlateContentTypeEnum.EMPTY.getValue());
        }
    }

    /**
     * 获取胶板时间列表
     *
     * @param experimentId
     * @param plateId
     * @param labId
     * @return
     */
    public List<PlateTimerVo> getPlateTimerList(Long experimentId, Long plateId, Long labId) {

        assertExperimentExist(experimentId, labId, null);

        assertPlateExistAndGet(experimentId, plateId);

        List<PlateGridTimer> plateGridTimers = plateGridTimerService.selectList(plateId);

        return plateGridTimers.stream().map(timer -> {
            PlateTimerVo timerVo = new PlateTimerVo();
            timerVo.setId(timer.getId());
            timerVo.setStepTimerId(timer.getStepTimerId());
            timerVo.setTime(timer.getTime());
            return timerVo;
        }).collect(toList());
    }

    private Plate assertPlateExistAndGet(Long experimentId, Long plateId) {
        Plate plate = plateService.selectById(plateId);
        Assert.notNull(plate, "多孔板不存在！");
        Assert.isTrue(plate.getExperimentId().equals(experimentId), "多孔板不属于该实验！");
        return plate;
    }

    private void assertExperimentExist(Long experimentId, Long labId, Long labMemberId) {
        Experiment experiment = experimentService.selectById(experimentId);
        Assert.notNull(experiment, "实验不存在！");
        Assert.isTrue(experiment.getLabId().equals(labId), "实验不属于该实验室！");
        if (labMemberId != null) {
            Assert.isTrue(experiment.getExecutorId().equals(labMemberId), "成员不是当前实验执行人！");
        }
    }

    private PlateGrid assertPlateGridExistAndGet(Long plateId, Long gridId) {
        PlateGrid grid = plateGridService.selectById(gridId);
        Assert.notNull(grid, "孔位不存在！");
        Assert.isTrue(grid.getPlateId().equals(plateId), "孔位不属于该多孔板！");
        return grid;
    }

    /**
     * 多孔板删除计时器
     *
     * @param experimentId
     * @param stepTimerId
     * @param gridIds
     * @param labId
     * @param labMemberId
     * @return
     */
    public void deleteTimer(Long experimentId, Long stepTimerId, Long plateId, List<Long> gridIds, Long labId, Long labMemberId) {

        assertExperimentExist(experimentId, labId, labMemberId);

        Plate plate = plateService.selectById(plateId);
        Assert.notNull(plate, "plate does not exist!");
        Assert.isTrue(plate.getExperimentId().equals(experimentId), "plate does not belong to this experiment!");

        if (!plate.getType().equals(PlateTypeEnum.GUM_PLATE.getValue())) {

            plateGridTimerService.deleteInGridIds(stepTimerId, gridIds);

            experimentStepTimerLogic.setGridNotUseForTime(gridIds);
        } else {
            plateGridTimerService.deleteInPlate(stepTimerId, plateId);
        }

    }

    /**
     * 多孔板删除试剂/样品
     *
     * @param experimentId
     * @param sourceId
     * @param type
     * @param gridIds
     * @param labId
     * @param labMemberId
     * @return
     */
    public void deleteReagent(Long experimentId, Long sourceId, Integer type, List<Long> gridIds, Long labId, Long labMemberId) {

        assertExperimentExist(experimentId, labId, labMemberId);

        plateGridReagentService.deleteInGridIds(sourceId, type, gridIds);

        setGridNotUseForReagentAndTime(gridIds);
    }

    public void setGridNotUseForReagentAndTime(List<Long> gridIds) {
        List<PlateGrid> plateGrids = plateGridService.selectList(gridIds);

        //所有GridTimer
        List<PlateGridTimer> timers = plateGridTimerService.selectList(gridIds);
        List<PlateGridReagent> reagents = plateGridReagentService.selectList(gridIds);

        List<PlateGrid> update = new ArrayList<>();
        gridIds.forEach(gridId -> {
            long rct = timers.stream().filter(e -> e.getPlateGridId().equals(gridId)).count();
            long tmct = reagents.stream().filter(e -> e.getPlateGridId().equals(gridId)).count();

            if (rct == 0 && tmct == 0) {
                PlateGrid grid = plateGrids.stream().filter(e -> e.getId().equals(gridId)).findFirst().orElse(null);
                if (grid != null) {
                    grid.setIsUsed(BoolEnum.FALSE.getValue());
                    update.add(grid);
                }
            }
        });

        plateGridService.updateBatch(update);
    }

    /**
     * 多孔板删除文本
     *
     * @param experimentId
     * @param stepTextId
     * @param gridIds
     * @param labId
     * @param labMemberId
     * @return
     */
    public void deleteText(Long experimentId, Long stepTextId, List<Long> gridIds, Long labId, Long labMemberId) {

        assertExperimentExist(experimentId, labId, labMemberId);

        plateGridTextService.deleteInGridIds(stepTextId, gridIds);

        setGridNotUseForText(gridIds);
    }

    public void setGridNotUseForText(List<Long> gridIds) {
        List<PlateGrid> plateGrids = plateGridService.selectList(gridIds);

        //所有text
        List<PlateGridText> texts = plateGridTextService.selectList(gridIds);
        List<PlateGridTimer> timers = plateGridTimerService.selectList(gridIds);

        List<PlateGrid> update = new ArrayList<>();
        gridIds.forEach(gridId -> {
            long tct = texts.stream().filter(e -> e.getPlateGridId().equals(gridId)).count();
            long rct = timers.stream().filter(e -> e.getPlateGridId().equals(gridId)).count();
            if (tct == 0 && rct == 0) {
                PlateGrid grid = plateGrids.stream().filter(e -> e.getId().equals(gridId)).findFirst().orElse(null);
                if (grid != null) {
                    grid.setIsUsed(BoolEnum.FALSE.getValue());
                    update.add(grid);
                }
            }
        });

        plateGridService.updateBatch(update);
    }

    public List<Plate> getPlateList(Long experimentId) {
        return plateService.getPlateList(experimentId);
    }

    /**
     * 多孔板添加覆盖内容
     */
    public void gumPlateGridModify(GumGridModify modify, Long labId, Long labMemberId) {

        Plate plate = plateService.selectById(modify.getPlateId());
        Assert.notNull(plate, "plate does not exist!");
        Assert.isTrue(plate.getType().equals(PlateTypeEnum.GUM_PLATE.getValue()), "plate is not a gum plate!");
        PlateGrid grid = plateGridService.selectById(modify.getGridId());
        Assert.notNull(grid, "grid does not exist!");
        Assert.isTrue(grid.getPlateId().equals(plate.getId()), String.format("grid ID[%s] does not belong to plate ID[%s]", grid.getId(), plate.getId()));

        List<PlateGridReagent> reagents = plateGridReagentService.selectList(plate.getId());
        List<PlateGridText> texts = plateGridTextService.selectList(plate.getId());

        boolean isText = texts.size() > 0 && reagents.size() == 0;
        boolean isEmpty = texts.size() == 0 && reagents.size() == 0;
        Assert.isTrue(!isEmpty, "胶板未添加过内容，无法替换!");

        if (modify.getStepReagentId() != null) {

            //修改添加的试剂
            Assert.isTrue(modify.getType() != null && modify.getAmount() != null, "请求参数缺失!");
            Assert.isTrue(modify.getAmount().toString().length() < 10, "试剂用量大小超限");
            Assert.isTrue(!isText, "胶板中为文本,无法替换为试剂！");

            //删除已存在
            reagents.stream().filter(e -> e.getPlateGridId().equals(grid.getId())).findFirst()
                    .ifPresent(exist -> plateGridReagentService.deleteById(exist.getId()));

            //新增
            PlateGridReagent reagent = new PlateGridReagent();
            Map<Integer, String> unitMap = dictService.getUnitMap();
            if (modify.getType().equals(ExperimentStepMaterialTypeEnum.STEP_REAGENT.getType())) {
                ExperimentStepReagent experimentStepReagent = experimentStepReagentService.selectById(modify.getStepReagentId());
                Assert.notNull(experimentStepReagent, "Step Reagent does not exist!");
                Assert.isTrue(experimentStepReagent.getExperimentId().equals(modify.getExperimentId()), "试剂不属于该实验步骤！");
                reagent.setPlateId(plate.getId());
                reagent.setPlateGridId(grid.getId());
                reagent.setFirstReagentTypeId(experimentStepReagent.getFirstReagentTypeId());
                reagent.setSourceId(experimentStepReagent.getId());
                reagent.setType(ExperimentStepMaterialTypeEnum.STEP_REAGENT.getType());
                reagent.setClassify(experimentStepReagent.getClassify());
                reagent.setName(experimentStepReagent.getName());
                reagent.setAlias(experimentStepReagent.getAlias());
                reagent.setUnit(experimentStepReagent.getUnit());
                reagent.setAmount(modify.getAmount().compareTo(BigDecimal.ZERO) > 0 ? modify.getAmount() : BigDecimal.ZERO);

            } else if (modify.getType().equals(ExperimentStepMaterialTypeEnum.EXPERIMENT_SAMPLE.getType())) {
                ExperimentBill experimentBill = experimentBillService.selectById(modify.getStepReagentId());
                Assert.notNull(experimentBill, "Step Reagent does not exist!");
                Assert.isTrue(experimentBill.getExperimentId().equals(modify.getExperimentId()), "样品不属于该实验！");
                reagent.setPlateId(plate.getId());
                reagent.setPlateGridId(grid.getId());
                reagent.setFirstReagentTypeId(experimentBill.getFirstReagentTypeId());
                reagent.setSourceId(experimentBill.getId());
                reagent.setType(ExperimentStepMaterialTypeEnum.EXPERIMENT_SAMPLE.getType());
                reagent.setClassify(experimentBill.getClassify());
                reagent.setName(experimentBill.getName());
                reagent.setAlias(experimentBill.getAlias());
                reagent.setUnit(unitMap.get(experimentBill.getUnit()));
                reagent.setAmount(modify.getAmount().compareTo(BigDecimal.ZERO) > 0 ? modify.getAmount() : BigDecimal.ZERO);
            } else {
                throw new BrillilabException(ResultEnum.DATA_NOT_EXIST, "type does not exist!");
            }
            plateGridReagentService.insert(reagent);

        } else if (modify.getStepTextId() != null) {
            //修改添加的文本
            Assert.isTrue(isText, "胶板中为试剂,无法替换为试剂！");
            //删除存在文本
            texts.stream().filter(e -> e.getPlateGridId().equals(grid.getId())).findFirst()
                    .ifPresent(exist -> plateGridTextService.deleteById(exist.getId()));
            //新增新文本
            ExperimentStepText stepText = experimentStepTextService.selectById(modify.getStepTextId());
            Assert.notNull(stepText, "Step Text does not exist!");
            PlateGridText gridText = new PlateGridText();
            gridText.setPlateId(plate.getId());
            gridText.setPlateGridId(grid.getId());
            gridText.setStepTextId(stepText.getId());
            gridText.setContent(stepText.getContent());
            plateGridTextService.insert(gridText);
        }
    }

    /**
     * 多孔板添加实验分组
     *
     * @param vo
     * @return
     */
    public boolean addPlateGridGroup(PlateGridGroupAddVo vo) {
        Plate plate = plateService.selectById(vo.getPlateId());
        Assert.notNull(plate, "多孔板不存在!");
        Assert.isTrue(plate.getExperimentId().equals(vo.getExperimentId()), "多孔板不属于该实验!");

        //添加实验分组的孔位是否属于该多孔板
//        List<Long> plateGridIdList = plateGridService.selectList(vo.getPlateId()).stream().map(PlateGrid::getId).collect(toList());

        //删除以前的分组信息
        List<ExperimentGroupGridVo> items=vo.getPlateGridGroups();
        List<Long> gridIdList = items.stream().map(ExperimentGroupGridVo::getPlateGridId).collect(toList());
        plateGridGroupService.deleteByGridIds(gridIdList);

        //判断多孔板是否已经添加文本试剂定时等
        assertCanAddExperimentGroup(plate.getId());

        List<Long> experimentGroupIdList = experimentGroupService.selectList(vo.getExperimentId()).stream().map(ExperimentGroup::getId).collect(toList());
        Assert.isTrue(CollectionUtils.isNotEmpty(experimentGroupIdList), "未设置实验分组，请先添加实验分组！");

        List<Long> addGroupIdList = items.stream().map(ExperimentGroupGridVo::getExperimentGroupId).distinct().collect(toList());
        Assert.isTrue(experimentGroupIdList.containsAll(addGroupIdList), "不能添加其他实验的实验分组！");

        List<PlateGridGroup> plateGridGroups = new ArrayList<>();
        items.forEach(groupGrid -> {
            PlateGridGroup plateGridGroup = new PlateGridGroup();
            plateGridGroup.setPlateId(vo.getPlateId());
            plateGridGroup.setPlateGridId(groupGrid.getPlateGridId());
            plateGridGroup.setExperimentGroupId(groupGrid.getExperimentGroupId());
            plateGridGroups.add(plateGridGroup);
        });

        //设置grid已用
        plateGridService.setIsUsed(gridIdList,BoolEnum.TRUE.getValue());

        return plateGridGroupService.insertBatch(plateGridGroups);
    }

    private void assertCanAddExperimentGroup(Long plateId) {
        int reagentCount = plateGridReagentService.selectCountOnePlate(plateId);
        int timerCount=plateGridTimerService.selectCountOnePlate(plateId);
        int textCount=plateGridTextService.selectCountOnePlate(plateId);
        Assert.isTrue(reagentCount == 0 && timerCount == 0 && textCount == 0, "已存在试剂/样品/分组，不可继续加入分组！");
    }

    /**
     * 多孔板删除实验分组
     *
     * @param plateGridGroupId
     * @return
     */
    public boolean deletePlateGridGroup(List<Long> plateGridGroupId) {
        List<PlateGridGroup> groups = plateGridGroupService.selectListByIds(plateGridGroupId);
        List<Long> gridIds=groups.stream().map(PlateGridGroup::getPlateGridId).distinct().collect(toList());
        plateGridGroupService.deleteBatchById(plateGridGroupId);
        plateGridService.setIsUsed(gridIds,BoolEnum.FALSE.getValue());
        return true;
    }
}
